home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Libraries / WASTE 1.1 / WELineLayout.c < prev    next >
Encoding:
Text File  |  1994-11-03  |  29.0 KB  |  1,145 lines  |  [TEXT/MPCC]

  1. // { WASTE PROJECT: }
  2. // { Line Layout, Creation & Destruction, Getting and Setting Variables, etc. }
  3.  
  4. // { Copyright © 1993-1994 Marco Piovanelli }
  5. // { All Rights Reserved }
  6.  
  7. // C conversion by Dan Crevier
  8.  
  9. #include "WASTEIntf.h"
  10. #include <Palettes.h>
  11. #include <QDOffscreen.h>
  12. #include <GestaltEqu.h>
  13. #include <Drag.h>
  14.  
  15. pascal OSErr _WERegisterWithTSM(WEHandle hWE)
  16. {
  17.     // { the WE record must be already locked }
  18.     WEPtr pWE;
  19.     OSType typeList[1];
  20.     OSErr err;
  21.  
  22.     pWE = *hWE;
  23.  
  24.     // { do nothing if the Text Services Manager isn't available }
  25.     if (BTST(pWE->flags, weFHasTextServices))
  26.     {
  27.         typeList[0] = kTextService;
  28.         err = NewTSMDocument(1, typeList, &pWE->tsmReference, (long)hWE);
  29.         if (err != noErr) 
  30.         {
  31.             // { we don't consider it an error if our client application isn't TSM-aware }
  32.             if (err != tsmNeverRegisteredErr) 
  33.             {
  34.                 goto cleanup;
  35.             }
  36.         }
  37.     }
  38.  
  39.     // { clear result code }
  40.     err = noErr;
  41.  
  42. cleanup:
  43.     // { return result code }
  44.     return err;
  45. }
  46.  
  47. pascal void WEStopInlineSession(WEHandle hWE)
  48. {
  49.     TSMDocumentID tsmReference;
  50.  
  51.     tsmReference = (*hWE)->tsmReference;
  52.     if (tsmReference != nil) 
  53.     {
  54.         FixTSMDocument(tsmReference);
  55.     }
  56. }
  57.  
  58. pascal OSErr WENew(LongRect *destRect, LongRect *viewRect, short flags, WEHandle *hWE)
  59. {
  60.     WEPtr pWE;
  61.     short allocFlags;
  62.     long weFlags;
  63.     long response;
  64.     Rect r;
  65.     OSErr err;
  66.     
  67.     pWE = nil;
  68.     weFlags = flags;
  69.     allocFlags = kAllocClear;
  70.  
  71.     // { allocate the WE record }
  72.     err = _WEAllocate(sizeof(WERec), allocFlags, (Handle *)hWE);
  73.     if (err != noErr) 
  74.     {
  75.         goto cleanup;
  76.     }
  77.     
  78.     // { lock it down }
  79.     HLock((Handle)*hWE);
  80.     pWE = **hWE;
  81.  
  82.     // { get active port }
  83.     GetPort(&pWE->port);
  84.  
  85.     // { determine whether temporary memory should be used for data structures }
  86.     if (BTST(weFlags, weFUseTempMem))
  87.     { 
  88.         allocFlags = allocFlags + kAllocTemp;
  89.     }
  90.     
  91.     // { allocate the text handle (initially empty) }
  92.     err = _WEAllocate(0, allocFlags, (Handle *)&pWE->hText);
  93.     if (err != noErr) 
  94.     {
  95.         goto cleanup;
  96.     }
  97.  
  98.     // { allocate the line array }
  99.     err = _WEAllocate(2 * sizeof(LineRec), allocFlags, (Handle *)&pWE->hLines);
  100.     if (err != noErr) 
  101.     {
  102.         goto cleanup;
  103.     }
  104.  
  105.     // { allocate the style table }
  106.     err = _WEAllocate(sizeof(StyleTableElement), allocFlags, (Handle *)&pWE->hStyles);
  107.     if (err != noErr) 
  108.     {
  109.         goto cleanup;
  110.     }
  111.     
  112.     // { allocate the run array }
  113.     err = _WEAllocate(2 * sizeof(RunArrayElement), allocFlags, (Handle *)&pWE->hRuns);
  114.     if (err != noErr) 
  115.     {
  116.         goto cleanup;
  117.     }
  118.     
  119.     // { check for the presence of various system software features }
  120.     // { determine whether Color QuickDraw is available }
  121.     if (Gestalt(gestaltQuickdrawVersion, &response) == noErr) 
  122.     {
  123.         if (response >= gestalt8BitQD)
  124.         { 
  125.             BSET(weFlags, weFHasColorQD);
  126.         }
  127.     }
  128.     
  129.     // { determine whether the Text Services manager is available }
  130.     if (Gestalt(gestaltTSMgrVersion, &response) == noErr)
  131.     { 
  132.         BSET(weFlags, weFHasTextServices);
  133.     }
  134.     
  135.     // { determine if there are any non-Roman scripts enabled }
  136.     if (GetEnvirons(smEnabled) > 1) 
  137.     {
  138.         BSET(weFlags, weFNonRoman);
  139.     }
  140.     
  141.     // { determine whether a double-byte script is installed }
  142.     if (GetEnvirons(smDoubleByte) != 0) 
  143.     {
  144.         BSET(weFlags, weFDoubleByte);
  145.     }
  146.     
  147.     // { determine whether the Drag Manager is available }
  148.     if (Gestalt(gestaltDragMgrAttr, &response) == noErr)
  149.     {
  150.         if (BTST(response, gestaltDragMgrPresent))
  151.                 BSET(weFlags, weFDragAndDrop);
  152.     }
  153.  
  154.     // { initialize miscellaneous fields of the WE record }
  155.     pWE->nLines = 1;
  156.     pWE->nStyles = 1;
  157.     pWE->nRuns = 1;
  158.     pWE->viewRect = *viewRect;
  159.     pWE->destRect = *destRect;
  160.     pWE->flags = weFlags;
  161.     pWE->tsmAreaStart = kInvalidOffset;
  162.     pWE->tsmAreaEnd = kInvalidOffset;
  163.     pWE->dragCaretOffset = kInvalidOffset;
  164.  
  165.     // { create a region to hold the view rectangle }
  166.     pWE->viewRgn = NewRgn();
  167.     WELongRectToRect(viewRect, &r);
  168.     RectRgn(pWE->viewRgn, &r);
  169.  
  170.     // { initialize the style run array }
  171.     (*pWE->hRuns)[1].runStart = 1;
  172.     (*pWE->hRuns)[1].styleIndex = -1;
  173.  
  174.     // { initialize the style table }
  175.     (*pWE->hStyles)[0].refCount = 1;
  176.  
  177.     // { copy text attributes from the active graphics port }
  178.     (*pWE->hStyles)[0].info.runStyle.tsFont = pWE->port->txFont;
  179.     (*pWE->hStyles)[0].info.runStyle.tsSize = pWE->port->txSize;
  180.     (*pWE->hStyles)[0].info.runStyle.tsFace = ((GrafPtr1)pWE->port)->txFace;
  181.     if (BTST(weFlags, weFHasColorQD))
  182.     { 
  183.         GetForeColor(&(*pWE->hStyles)[0].info.runStyle.tsColor);
  184.     }
  185.     _WEFillFontInfo(pWE->port, &(*pWE->hStyles)[0].info);
  186.  
  187.     // { initialize the line array }
  188.     err = WECalText(*hWE);
  189.     if (err != noErr) 
  190.     {
  191.         goto cleanup;
  192.     }
  193.     
  194.     // { register with the Text Services Manager }
  195.     err = _WERegisterWithTSM(*hWE);
  196.     if (err != noErr) 
  197.     {
  198.         goto cleanup;
  199.     }
  200.  
  201.     // { unlock the WE record }
  202.     HUnlock((Handle)*hWE);
  203.  
  204.     // { skip clean-up section }
  205.     return noErr;
  206.  
  207. cleanup:
  208.     // { clean up }
  209.     if (pWE != nil) 
  210.     {
  211.         _WEForgetHandle((Handle *)&pWE->hText);
  212.         _WEForgetHandle((Handle *)&pWE->hLines);
  213.         _WEForgetHandle((Handle *)&pWE->hStyles);
  214.         _WEForgetHandle((Handle *)&pWE->hRuns);
  215.         if (pWE->viewRgn != nil) 
  216.         {
  217.             DisposeRgn(pWE->viewRgn);
  218.         }
  219.     }
  220.     _WEForgetHandle((Handle *)hWE);
  221.  
  222.     return err;
  223. }
  224.  
  225. pascal void WEDispose(WEHandle hWE)
  226. {
  227.     WEPtr pWE;
  228.     StyleTablePtr pTable;
  229.     long index;
  230.  
  231.     // { sanity check: make sure WE isn't NIL }
  232.     if (hWE == nil)
  233.     {
  234.         return;
  235.     } 
  236.  
  237.     // { lock the WE record }
  238.     HLock((Handle)hWE);
  239.     pWE = *hWE;
  240.  
  241.     // { clear the Undo buffer }
  242.     WEClearUndo(hWE);
  243.  
  244.     // { unregister with the Text Services Manager }
  245.     if (pWE->tsmReference != nil)
  246.     {
  247.         DeleteTSMDocument(pWE->tsmReference);
  248.         pWE->tsmReference = nil;
  249.     }
  250.  
  251.     // { dispose of the offscreen graphics world }
  252.     if (pWE->offscreenPort != nil) 
  253.     {
  254.         DisposeGWorld((GWorldPtr)pWE->offscreenPort);
  255.         pWE->offscreenPort = nil;
  256.     }
  257.     
  258.     if (pWE->hStyles != nil)
  259.     {
  260.         // { lock the style table }
  261.         HLock((Handle)pWE->hStyles);
  262.         pTable = *pWE->hStyles;
  263.  
  264.         // { walk the style table, disposing of all embedded objects referenced there }
  265.         index = 0;
  266.         while (index < pWE->nStyles)
  267.         {
  268.             if (pTable[index].refCount > 0)
  269.             {
  270.                 if (_WEFreeObject((WEObjectDescHandle)(pTable[index].info.runStyle.tsObject))
  271.                      != noErr)
  272.                 {
  273.                                     ;        //{ don't known what to do with errors }
  274.                 }
  275.             }
  276.             index = index + 1;
  277.         }
  278.     }
  279.     
  280.     // { dispose of auxiliary data structures }
  281.     _WEForgetHandle((Handle *)&pWE->hText);
  282.     _WEForgetHandle((Handle *)&pWE->hLines);
  283.     _WEForgetHandle((Handle *)&pWE->hStyles);
  284.     _WEForgetHandle((Handle *)&pWE->hRuns);
  285.     DisposeRgn(pWE->viewRgn);
  286.  
  287.     // { dispose of the WE record }
  288.     DisposeHandle((Handle)hWE);
  289.  
  290. }
  291.  
  292. pascal OSErr _WERemoveLine(long lineIndex, WEPtr pWE)
  293. {
  294.     OSErr retval;
  295.     
  296.     // { remove the specified element from the line array }
  297.  
  298.     // { do the removal (errors returned by _WERemoveSlot can be safely ignored) }
  299.     retval = _WERemoveSlot((Handle)pWE->hLines, lineIndex, sizeof(LineRec));
  300.  
  301.     // { decrement line count }
  302.     pWE->nLines = pWE->nLines - 1;
  303.     
  304.     return retval;
  305. }
  306.  
  307. pascal OSErr InsertLine(long lineIndex, LineRec *theLine, WEPtr pWE)
  308. {
  309.     // { insert the specified element in the line array }
  310.  
  311.     OSErr err;
  312.  
  313.     // { do the insertion }
  314.     err = _WEInsertSlot((Handle)pWE->hLines, (Ptr)theLine, lineIndex, sizeof(LineRec));
  315.     if (err != noErr) 
  316.     {
  317.         return err;
  318.     }
  319.  
  320.     // { increment line count }
  321.     pWE->nLines = pWE->nLines + 1;
  322.  
  323.     return noErr;
  324. }
  325.  
  326. pascal void _WEBumpOrigin(long lineIndex, long deltaOrigin, WEPtr pWE)
  327. {
  328.     long *pOrigin;
  329.     long nLines;
  330.  
  331.     pOrigin = &((*pWE->hLines)[lineIndex].lineOrigin);
  332.  
  333.     // { loop through the line run array adjusting the lineOrigin fields }
  334.     nLines = pWE->nLines;
  335.     while (lineIndex <= nLines)
  336.     {
  337.         *pOrigin = *pOrigin + deltaOrigin;
  338.         pOrigin = (long *)((long)pOrigin + sizeof(LineRec));
  339.         lineIndex = lineIndex + 1;
  340.     }
  341. }
  342.  
  343. pascal long _WEFindLineBreak(long lineStart, WEHandle hWE)
  344. {
  345.     // { Find where to break the line beginning at lineStart }
  346.     // { the WE record and the text must be already locked }
  347.     // { the current graphics port must be already set up correctly }
  348.  
  349.     WEPtr pWE;
  350.     Ptr pText;
  351.     long offset, breakOffset;
  352.     long textLength;
  353.     long remainingLength;
  354.     long segmentStart, segmentEnd;
  355.     long runIndex;
  356.     WERunInfo runInfo;
  357.     Fixed pixelWidth;
  358.     ScriptCode script, previousScript;
  359.     Boolean isBreak;
  360.  
  361.     pWE = *hWE;
  362.     offset = lineStart;
  363.     pText = *pWE->hText + offset;
  364.     remainingLength = pWE->textLength - offset;
  365.  
  366.     // { find the style run index corresponding to the first segment on this line }
  367.     runIndex = _WEOffsetToRun(offset, hWE);
  368.  
  369.     // { initialize pixelWidth to the width of the destination rectangle, as a Fixed quantity }
  370.     pixelWidth = BSL(pWE->destRect.right - pWE->destRect.left, 16);
  371.  
  372.     // { STYLE SEGMENT LOOP }
  373.     do
  374.     {
  375.  
  376.         // { get style run information for the current style run }
  377.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  378.         runIndex = runIndex + 1;
  379.  
  380.         // { set text attributes in the graphics port }
  381.         TextFont(runInfo.runAttrs.runStyle.tsFont);
  382.         TextFace(runInfo.runAttrs.runStyle.tsFace);
  383.         TextSize(runInfo.runAttrs.runStyle.tsSize);
  384.  
  385.         // { if we're handling multiscript text, keep track of script boundaries }
  386.         if (BTST(pWE->flags, weFNonRoman))
  387.         { 
  388.             // { what is the script for this segment? }
  389.             script = Font2Script(runInfo.runAttrs.runStyle.tsFont);
  390.  
  391.             // { have we crossed a script run boundary in the middle of a line? }
  392.             if ((runInfo.runStart > offset) && (script != previousScript))
  393.             {
  394.                 // { leave behind the all previous segments on this line }
  395.                 offset = runInfo.runStart;
  396.                 pText = *pWE->hText + offset;
  397.                 remainingLength = pWE->textLength - offset;
  398.             }
  399.             previousScript = script;
  400.         } // { if non-Roman }
  401.  
  402.         // { we'll pass textLength as the second parameter to StyledLineBreak }
  403.         // { although this parameter is declared as a long, StyledLineBreak uses only }
  404.         // { the low word, so make sure it doesn't trespass the 32,767 byte threshold! }
  405.         textLength = _WEPinInRange(remainingLength, 0, SHRT_MAX);
  406.  
  407.         // { calculate segmentStart and segmentEnd relative to offset }
  408.         segmentStart = _WEPinInRange(runInfo.runStart - offset, 0, textLength);
  409.         segmentEnd = _WEPinInRange(runInfo.runEnd - offset, 0, textLength);
  410.  
  411.         // { set breakOffset to a non-zero value for the first script run on the line, }
  412.         // { set it to zero for all subsequent script runs }
  413.         breakOffset = (offset == lineStart);
  414.  
  415.         if (runInfo.runAttrs.runStyle.tsObject != nil)
  416.         {
  417.             // { EMBEDDED OBJECT }
  418.             // { subtract object width from pixelWidth }
  419.             pixelWidth = pixelWidth - 
  420.                 BSL((*(WEObjectDescHandle)(runInfo.runAttrs.runStyle.tsObject))->objectSize.h, 16);
  421.             
  422.             // { stop looping if pixelWidth has gone negative }
  423.             isBreak = (pixelWidth < 0);
  424.  
  425.             if (isBreak)
  426.                 breakOffset = segmentStart; // { break line before the object }
  427.             else
  428.                 breakOffset = segmentEnd;    // { break line after the object }
  429.         }
  430.         else
  431.         {
  432.             // { REGULAR TEXT }
  433.             // { StyledLineBreak does the work for us }
  434.             isBreak = (StyledLineBreak(pText, textLength, segmentStart, segmentEnd, 0, &pixelWidth, &breakOffset)
  435.                         != smBreakOverflow);
  436.  
  437.         }
  438.  
  439.         // { break the line anyway when we reach the end of the text }
  440.         if (segmentEnd >= remainingLength)
  441.             isBreak = true;
  442.  
  443.     } while (!isBreak);
  444.  
  445.     // { return the offset from lineStart to the break point }
  446.     return (offset - lineStart) + breakOffset;
  447. }
  448.  
  449. pascal void _WECalcHeights(long rangeStart, long rangeEnd, short *lineAscent, short *lineDescent,
  450.         WEHandle hWE)
  451. {
  452.     // { Find the maximum ascent and descent values between rangeStart and rangeEnd }
  453.     // { the WE record must be already locked }
  454.     // { the current graphics port must be already set up correctly }
  455.  
  456.     long runIndex;
  457.     WERunInfo runInfo;
  458.     short runAscent, runDescent;
  459.  
  460.     *lineAscent = 1;
  461.     *lineDescent = 1;
  462.  
  463.     // { find the style run index corresponding to the first segment on this line }
  464.     runIndex = _WEOffsetToRun(rangeStart, hWE);
  465.  
  466.     // { STYLE SEGMENT LOOP }
  467.     do
  468.     {
  469.         // { get style run information for the current style run }
  470.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  471.         runIndex = runIndex + 1;
  472.  
  473.         // { calculate ascent and descent (actually, descent + leading) values for this style run }
  474.         if (runInfo.runAttrs.runStyle.tsObject != nil)
  475.         {
  476.             // { EMBEDDED OBJECT }
  477.             runAscent = (*(WEObjectDescHandle)(runInfo.runAttrs.runStyle.tsObject))->objectSize.v;
  478.             runDescent = 0;
  479.         }
  480.         else
  481.         {
  482.             // { REGULAR TEXT }
  483.             runAscent = runInfo.runAttrs.runAscent;
  484.             runDescent = runInfo.runAttrs.runHeight - runAscent;
  485.         }
  486.         
  487.         // { save the maximum values in lineAscent and lineDescent }
  488.         if (runAscent > *lineAscent) 
  489.         {
  490.             *lineAscent = runAscent;
  491.         }
  492.         if (runDescent > *lineDescent) 
  493.         {
  494.             *lineDescent = runDescent;
  495.         }
  496.         
  497.         // { keep looping until we reach rangeEnd }
  498.     } while (runInfo.runEnd < rangeEnd);
  499. }
  500.  
  501. pascal OSErr _WERecalBreaks(long *startLine, long *endLine, WEHandle hWE)
  502. {
  503.     // { Recalculates line breaks, line heights and ascents for all the text or for a portion of it. }
  504.     // { On entry, startLine and endLine define a range of lines to recalculate. }
  505.     // { On exit, startLine to endLine defines the range of lines actually recalculated }
  506.     // { the WE record must already be locked }
  507.  
  508.     WEPtr pWE;
  509.     LinePtr pLine;
  510.     LineRec lineInfo, oldLineInfo;
  511.     long lineIndex;
  512.     long recalThreshold;
  513.     long lineOffset;
  514.     short lineAscent, lineDescent;
  515.     Boolean saveTextLock;
  516.     QDEnvironment saveEnvironment;
  517.     OSErr err;
  518.     OSErr retval;
  519.  
  520.     retval = noErr;
  521.     pWE = *hWE;
  522.  
  523.     // { lock the text }
  524.     saveTextLock = _WESetHandleLock(pWE->hText, true);
  525.  
  526.     // { find the character offset that must be necessarily reached before we can }
  527.     // { even consider the possibility of stopping the recalculation process }
  528.     // { this offset, recalThreshold, is the last character on endLine _before_ recalculation }
  529.     lineIndex = _WEPinInRange(*endLine, 0, pWE->nLines - 1);
  530.     recalThreshold = (*pWE->hLines)[lineIndex + 1].lineStart;
  531.  
  532.     // { we start recalculating line breaks from the line actually _preceding_ startLine, }
  533.     // { since editing startLine may cause part of its text to fit on the preceding line }
  534.     lineIndex = _WEPinInRange(*startLine - 1, 0, pWE->nLines - 1);
  535.  
  536.     // { find where in the text recalculation should begin }
  537.     lineInfo = (*pWE->hLines)[lineIndex];
  538.  
  539.     // { save the QuickDraw environment }
  540.     _WESaveQDEnvironment(pWE->port, false, &saveEnvironment);
  541.  
  542.     // { MAIN LINE BREAKING LOOP }
  543.     do
  544.     {
  545.         // { find where to break the current line }
  546.         lineOffset = _WEFindLineBreak(lineInfo.lineStart, hWE);
  547.  
  548.         // { make sure we advance at least by one character (unless we reached the end of text) }
  549.         if (lineOffset <= 0)
  550.         {
  551.             if (lineInfo.lineStart < pWE->textLength)
  552.                 lineOffset = 1;
  553.         }
  554.         
  555.         // { calculate ascent and descent values for this line }
  556.         _WECalcHeights(lineInfo.lineStart, lineInfo.lineStart + lineOffset, &lineAscent, &lineDescent, hWE);
  557.  
  558.         // { save the maximum line ascent for this line in the line array }
  559.         pLine = &(*pWE->hLines)[lineIndex];
  560.         pLine->lineAscent = lineAscent;
  561.  
  562.         // { increment counters (go to the next line array entry) }
  563.         lineIndex = lineIndex + 1;
  564.         lineInfo.lineStart = lineInfo.lineStart + lineOffset;
  565.         lineInfo.lineOrigin = lineInfo.lineOrigin + (lineAscent + lineDescent);
  566.         pLine++;
  567.         
  568.         // { compare the newly calculated line start with the old value }
  569.         // { if the new line start comes before the old line start, insert a new element }
  570.         oldLineInfo = *pLine;
  571.         if ((lineIndex > pWE->nLines) || (lineInfo.lineStart < oldLineInfo.lineStart)) 
  572.         {
  573.             err = InsertLine(lineIndex, &lineInfo, pWE);
  574.  
  575.             // { clean up and exit if we ran out of memory }
  576.             if (err != noErr) 
  577.             {
  578.                 retval = err;
  579.                 goto cleanup;
  580.             }
  581.         }
  582.         else
  583.         {
  584.             // { overwrite the old element }
  585.             pLine->lineStart = lineInfo.lineStart;
  586.             pLine->lineOrigin = lineInfo.lineOrigin;
  587.  
  588.             // { remove all further elements which have a lineStart field }
  589.             // { less than or equal to the current one }
  590.             while((lineIndex + 1 <= pWE->nLines) && 
  591.                 (lineInfo.lineStart >= (*pWE->hLines)[lineIndex + 1].lineStart))
  592.             {
  593.                 err = _WERemoveLine(lineIndex + 1, pWE);
  594.             }
  595.  
  596.             // { if the new line start is the same as the old one... }
  597.             if (lineInfo.lineStart == oldLineInfo.lineStart) 
  598.             {
  599.                 // { ...and recalThreshold has been reached, we can stop recalculating line breaks }
  600.                 if (lineInfo.lineStart >= recalThreshold) 
  601.                 {
  602.                     // { although line breaks need not be changed from lineIndex on, }
  603.                     // { the lineOrigin fields may need to be changed }
  604.                     if (lineInfo.lineOrigin != oldLineInfo.lineOrigin) 
  605.                     {
  606.                         _WEBumpOrigin(lineIndex + 1, lineInfo.lineOrigin - oldLineInfo.lineOrigin, pWE);
  607.                     }
  608.                     
  609.                     // { exit from the line breaking loop }
  610.                     goto cleanup;
  611.                 }
  612.             }
  613.             else
  614.             {
  615.                 // { otherwise, the new line start comes after the old line start... }
  616.                 // { if the current line is the one preceding startLine, warn our caller about this }
  617.                 if ((lineIndex > 0) && (lineIndex == *startLine)) 
  618.                 {
  619.                     *startLine = lineIndex - 1;
  620.                 }
  621.             }
  622.         }
  623.     } while(lineInfo.lineStart < pWE->textLength);
  624.  
  625. cleanup:
  626.     // { set destRect.bottom to destRect.top + total text height }
  627.     pWE->destRect.bottom = pWE->destRect.top + WEGetHeight(0, pWE->nLines, hWE);
  628.  
  629.     // { quirk: if the last character in the text is a carriage return, the caret appears }
  630.     // { below the last line, so in this case we need to add the extra height to destRect.bottom }
  631.     if (WEGetChar(pWE->textLength - 1, hWE) == '\r')
  632.     {
  633.         pWE->destRect.bottom = pWE->destRect.bottom + 
  634.                                     WEGetHeight(pWE->nLines - 1, pWE->nLines, hWE);
  635.     }
  636.     
  637.     // { return through endLine the index of the last line affected by recalculation }
  638.     *endLine = lineIndex - 1;
  639.  
  640.     // { make sure startLine isn't greater than endLine }
  641.     if (*startLine > *endLine) 
  642.     {
  643.         *startLine = *endLine;
  644.     }
  645.     
  646.     // { unlock the text }
  647.     _WESetHandleLock(pWE->hText, saveTextLock);
  648.  
  649.     // { restore the QuickDraw environment }
  650.     _WERestoreQDEnvironment(&saveEnvironment);
  651.     return retval;
  652. }
  653.  
  654. Boolean SLCalcSlop(LinePtr pLine, WERunAttributesPtr pAttrs, Ptr pSegment, long segmentStart,
  655.     long segmentLength, JustStyleCode styleRunPosition, void *callbackData);
  656.  
  657.  
  658. Boolean SLCalcSlop(LinePtr pLine, WERunAttributesPtr pAttrs, Ptr pSegment, long segmentStart,
  659.     long segmentLength, JustStyleCode styleRunPosition, void *callbackData)
  660. {
  661.     Boolean isEndOfLine;
  662.     Boolean retval;
  663.     struct SLCalcSlopData *p = (struct SLCalcSlopData *) callbackData;
  664.     short segmentWidth;
  665.     Fixed segmentProportion;
  666.  
  667.     retval = false;                // { keep looping }
  668.  
  669.     // { see if this text segment ends with a carriage return, or if we've reached the }
  670.     // { end of the text (in which case we don't want any justification to take place) }
  671.     isEndOfLine = (segmentStart + segmentLength >= p->pWE->textLength) ||
  672.                     ( *((Ptr)pSegment + segmentLength - 1) == kEOL);
  673.  
  674.     // { if this is the first segment on the line, reset line totals }
  675.     if (styleRunPosition <= smLeftStyleRun) 
  676.     {
  677.         p->totalSlop = p->lineWidth;
  678.         p->totalProportion = 0;
  679.     }
  680.     
  681.     if (pAttrs->runStyle.tsObject != nil)
  682.     {
  683.         // { EMBEDDED OBJECT }
  684.         // { segment width is just object width; no extra space can be applied for justification }
  685.         segmentWidth = (*(WEObjectDescHandle)(pAttrs->runStyle.tsObject))->objectSize.h;
  686.         segmentProportion = 0;
  687.     }
  688.     else
  689.     {
  690.         //{ REGULAR TEXT }
  691.  
  692.         // { if this is the last segment on the line, strip trailing spaces }
  693.         if (!(styleRunPosition & 1)) 
  694.         {
  695.             segmentLength = VisibleLength(pSegment, segmentLength);
  696.         }
  697.         // { measure this segment }
  698.         segmentWidth = TextWidth(pSegment, 0, segmentLength);
  699.  
  700.         // { calculate the proportion of extra space to apply to this text segment }
  701.         segmentProportion = NPortionText(pSegment, segmentLength,
  702.             styleRunPosition, *(Point *)&kOneToOneScaling, *(Point *)&kOneToOneScaling);
  703.     }
  704.     
  705.     // keep track of line totals
  706.     p->totalSlop = p->totalSlop - segmentWidth;
  707.     p->totalProportion = p->totalProportion + segmentProportion;
  708.  
  709.     // { if this is the last segment on the line, save values in the line array }
  710.     if (!(styleRunPosition & 1)) 
  711.     {
  712.         // { make sure slop is non-negative }
  713.         if (p->totalSlop < 0)
  714.             p->totalSlop = 0;
  715.         pLine->lineSlop = p->totalSlop;
  716.  
  717.         if (isEndOfLine) 
  718.         {
  719.             pLine->lineJustAmount = 0;
  720.         }
  721.         else
  722.         {
  723.                 pLine->lineJustAmount = FixDiv(BSL(p->totalSlop, 16), p->totalProportion);
  724.         }
  725.     }
  726.     return retval;
  727. }
  728.  
  729.  
  730. pascal void _WERecalSlops(long firstLine, long lastLine, WEHandle hWE)
  731. {
  732.     // { Calculates the lineSlop and lineJustAmount fields }
  733.     // { of the line array for the specified lines }
  734.  
  735.     WEPtr pWE;
  736.     short lineWidth;
  737.     struct SLCalcSlopData callbackData;
  738.  
  739.     pWE = *hWE;
  740.     lineWidth = pWE->destRect.right - pWE->destRect.left;
  741.  
  742.     // { calculate slop and normalized slop proportion for all lines }
  743.     callbackData.lineWidth = lineWidth;
  744.     callbackData.pWE = pWE;
  745.     _WESegmentLoop(firstLine, lastLine, SLCalcSlop, (void *) &callbackData, hWE);
  746. }
  747.  
  748. pascal OSErr WECalText(WEHandle hWE)
  749. {
  750.     long startLine, endLine;
  751.     Boolean saveWELock;
  752.     OSErr err;
  753.  
  754.     // { lock WE record }
  755.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  756.  
  757.     // { recalculate all line breaks }
  758.     startLine = 0;
  759.     endLine = LONG_MAX;
  760.     err = _WERecalBreaks(&startLine, &endLine, hWE);
  761.  
  762.     // { recalculate line slops }
  763.     if (err == noErr) 
  764.     {
  765.         _WERecalSlops(startLine, endLine, hWE);
  766.     }
  767.  
  768.     // { unlock the WE record }
  769.     _WESetHandleLock((Handle)hWE, saveWELock);
  770.  
  771.     // { return result code }
  772.     return err;
  773. }
  774.  
  775. pascal OSErr WEUseText(Handle text, WEHandle hWE)
  776. {
  777.     WEPtr pWE;
  778.     long textLength;
  779.     Boolean saveWELock;
  780.  
  781.     // { lock the WE record }
  782.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  783.     pWE = *hWE;
  784.  
  785.     // { install the text }
  786.     _WEForgetHandle(&pWE->hText);
  787.     pWE->hText = text;
  788.     textLength = GetHandleSize(text);
  789.     pWE->textLength = textLength;
  790.     (*pWE->hRuns)[pWE->nRuns].runStart = textLength + 1;
  791.     (*pWE->hLines)[pWE->nLines].lineStart = textLength;
  792.     
  793.     // { unlock the WE record }
  794.     _WESetHandleLock((Handle)hWE, saveWELock);
  795.  
  796.     return noErr;
  797. }
  798.  
  799. pascal char WEGetAlignment(WEHandle hWE)
  800. {
  801.     return (*hWE)->alignment;
  802. }
  803.  
  804. pascal void WEGetSelection(long *selStart, long *selEnd, WEHandle hWE)
  805. {
  806.     WEPtr pWE;
  807.  
  808.     pWE = *hWE;
  809.     *selStart = pWE->selStart;
  810.     *selEnd = pWE->selEnd;
  811. }
  812.  
  813. pascal void WESetDestRect(LongRect *destRect, WEHandle hWE)
  814. {
  815.     (*hWE)->destRect = *destRect;
  816. }
  817.  
  818. pascal void WEGetDestRect(LongRect *destRect, WEHandle hWE)
  819. {
  820.     *destRect = (*hWE)->destRect;
  821. }
  822.  
  823. pascal void WESetViewRect(LongRect *viewRect, WEHandle hWE)
  824. {
  825.     WEPtr pWE;
  826.     Rect r;
  827.     
  828.     pWE = *hWE;
  829.     pWE->viewRect = *viewRect;
  830.  
  831.     // { keep the viewRgn in sync with the view rectangle }
  832.     WELongRectToRect(viewRect, &r);
  833.     RectRgn(pWE->viewRgn, &r);
  834. }
  835.  
  836. pascal void WEGetViewRect(LongRect *viewRect, WEHandle hWE)
  837. {
  838.     *viewRect = (*hWE)->viewRect;
  839. }
  840.  
  841. pascal long WEGetTextLength(WEHandle hWE)
  842. {
  843.     return (*hWE)->textLength;
  844. }
  845.  
  846. pascal long WECountLines(WEHandle hWE)
  847. {
  848.     return (*hWE)->nLines;
  849. }
  850.  
  851. pascal long WEGetHeight(long startLine, long endLine, WEHandle hWE)
  852. {
  853.     WEPtr pWE;
  854.     LineArrayPtr pLines;
  855.     long nLines;
  856.  
  857.     pWE = *hWE;
  858.     pLines = *pWE->hLines;
  859.     nLines = pWE->nLines;
  860.     startLine = _WEPinInRange(startLine, 0, nLines);
  861.     endLine = _WEPinInRange(endLine, 0, nLines);
  862.     _WEReorder(&startLine, &endLine);
  863.     return pLines[endLine].lineOrigin - pLines[startLine].lineOrigin;
  864. }
  865.  
  866. pascal Handle WEGetText(WEHandle hWE)
  867. {
  868.     return (*hWE)->hText;
  869. }
  870.  
  871. pascal char WEGetChar(long offset, WEHandle hWE)
  872. {
  873.     WEPtr pWE;
  874.  
  875.     pWE = *hWE;
  876.  
  877.     // { sanity check: make sure offset is withing allowed bounds }
  878.     if ((offset < 0) || (offset >= pWE->textLength))
  879.     {
  880.         return 0;
  881.     }
  882.  
  883.     // { get the specified character (actually, byte) }
  884.     return (*pWE->hText)[offset];
  885. }
  886.  
  887. pascal OSErr WECopyRange(long rangeStart, long rangeEnd, Handle hText, Handle
  888.                     hStyles, Handle hSoup, WEHandle hWE)
  889. {
  890.  
  891.     // { Make a copy of the specified range of text: store the characters in hText }
  892.     // { and the associated style scrap in hStyles.  The handles are resized as necessary. }
  893.     // { Specify NIL in hText or hStyles if you don't want the corresponding info returned. }
  894.  
  895.     WEPtr pWE;
  896.     long rangeLength;
  897.     long firstRun, nRuns, i;
  898.     long startChar;
  899.     WERunInfo info;
  900.     WESoup soupItem;
  901.     short *pElement;
  902.     WEObjectDescPtr pDesc;
  903.     Handle hObjectData;
  904.     Boolean saveDataLock;
  905.     Boolean saveWELock;
  906.     OSErr err;
  907.     TEStyleScrapElementPtr pElem2;
  908.  
  909.     // { lock the WE record }
  910.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  911.     pWE = *hWE;
  912.  
  913.     // { range-check parameters and reorder them if necessary }
  914.     rangeStart = _WEPinInRange(rangeStart, 0, pWE->textLength);
  915.     rangeEnd = _WEPinInRange(rangeEnd, 0, pWE->textLength);
  916.     _WEReorder(&rangeStart, &rangeEnd);
  917.     rangeLength = rangeEnd - rangeStart;
  918.  
  919.     if (hText != nil) 
  920.     {
  921.         // { resize the given handle }
  922.         SetHandleSize(hText, rangeLength);
  923.         err = MemError();
  924.         if (err != noErr) 
  925.         {
  926.             goto cleanup;
  927.         }
  928.  
  929.         // { copy the text range }
  930.         BlockMoveData((Ptr)*pWE->hText + rangeStart, (Ptr)*hText, rangeLength);
  931.  
  932.     }
  933.  
  934.     // { make the soup handle zero-length }
  935.     if (hSoup != nil)
  936.     {
  937.         SetHandleSize(hSoup, 0);
  938.         err = MemError();
  939.         if (err != noErr)
  940.                 goto cleanup;
  941.     }
  942.  
  943.     if (hStyles != nil || hSoup != nil) 
  944.     {
  945.         // { count how many style runs there are in the selection range }
  946.         firstRun = _WEOffsetToRun(rangeStart, hWE);
  947.         nRuns = _WEOffsetToRun(rangeEnd - 1, hWE) - firstRun + 1;
  948.  
  949.         if (hStyles != nil)
  950.         {
  951.             // { resize the given style scrap handle and lock it in high heap }
  952.             SetHandleSize((Handle)hStyles, ((long)nRuns * sizeof(ScrpSTElement)) + 2);
  953.             err = MemError();
  954.             if (err != noErr)
  955.                 goto cleanup;
  956.             HLock((Handle)hStyles);
  957.             pElement = (short *)*hStyles;
  958.             
  959.             // { fill in the style count in the style scrap }
  960.             // { *** POTENTIAL PROBLEM: if nRuns > 32767, scrpNStyles will be invalid *** }
  961.             *pElement = nRuns;
  962.             pElement++;
  963.         }
  964.         
  965.         pElem2 = (TEStyleScrapElementPtr)pElement;
  966.         // { loop through every style run in the selection range }
  967.         for(i = 0; i<nRuns; i++)
  968.         {
  969.             _WEGetIndStyle(firstRun + i, &info, hWE);
  970.             
  971.             // { calculate the start character for this style run, relative to the beginning of the range }
  972.             startChar = info.runStart - rangeStart;
  973.             if (startChar < 0)
  974.             {
  975.                 startChar = 0;
  976.                 info.runAttrs.runStyle.tsObject = nil;
  977.             }
  978.             if (hStyles != nil)
  979.             {
  980.                 info.runAttrs.runStyle.tsFlags = 0; // { don't export internal flags }
  981.                 pElem2->scrpStartChar = startChar;
  982.                 pElem2->scrpTEAttrs = *(TERunAttributes *)&info.runAttrs;
  983.                 pElem2++;
  984.             }
  985.             if (hSoup != nil)
  986.             {
  987.                 // { if this style run references an embedded object, append it to the "soup" }
  988.                 if (info.runAttrs.runStyle.tsObject != nil)
  989.                 {
  990.                     pDesc = *(WEObjectDescHandle)(info.runAttrs.runStyle.tsObject);
  991.                     hObjectData = pDesc->objectDataHandle;
  992.  
  993.                     // { fill in a soup item }
  994.                     _WEBlockClr((Ptr)&soupItem, sizeof(soupItem));
  995.                     soupItem.soupOffset = startChar;
  996.                     soupItem.soupType = pDesc->objectType;
  997.                     soupItem.soupSize = pDesc->objectSize;
  998.                     soupItem.soupDataSize = GetHandleSize(hObjectData);
  999.  
  1000.                     // { append it to the soup handle }
  1001.                     err = PtrAndHand(&soupItem, hSoup, sizeof(WESoup));
  1002.                     if (err != noErr)
  1003.                         goto cleanup;
  1004.  
  1005.                     // { append the actual object data to the soup handle }
  1006.                     saveDataLock = _WESetHandleLock(hObjectData, true);
  1007.                     err = PtrAndHand(*hObjectData, hSoup, soupItem.soupDataSize);
  1008.                     _WESetHandleLock(hObjectData, saveDataLock);
  1009.                     if (err != noErr)
  1010.                         goto cleanup;
  1011.                 }
  1012.             }
  1013.         }
  1014.     }
  1015.     // { clear result code }
  1016.     err = noErr;
  1017.  
  1018. cleanup:
  1019.     
  1020.     // { unlock the style scrap handle }
  1021.     if (hStyles != nil)
  1022.         HUnlock((Handle)hStyles);
  1023.  
  1024.     // { unlock the WE record }
  1025.     _WESetHandleLock((Handle)hWE, saveWELock);
  1026.     // { return result code }
  1027.     return err;
  1028. }
  1029.  
  1030. pascal OSErr WECopy(WEHandle hWE)
  1031. {
  1032.     // { Copy the selection range to the desk scrap }
  1033.  
  1034.     WEPtr pWE;
  1035.     AEDesc d[3];
  1036.     WEObjectDescHandle hObjectDesc;
  1037.     WEObjectDescPtr pDesc;
  1038.     Handle hItem;
  1039.     Size itemSize;
  1040.     short i, numTypes;
  1041.     Boolean saveWELock, saveDataLock;
  1042.     Boolean disposeData;
  1043.     OSErr err;
  1044.  
  1045.     d[0].dataHandle = nil;
  1046.     d[1].dataHandle = nil;
  1047.     d[2].dataHandle = nil;
  1048.     disposeData = false;
  1049.  
  1050.     // { lock the WE record }
  1051.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  1052.     pWE = *hWE;
  1053.  
  1054.     // { do nothing if the selection range is empty }
  1055.     if (pWE->selStart == pWE->selEnd) goto cleanup2;
  1056.     
  1057.     // { clear the desk scrap }
  1058.     err = ZeroScrap();
  1059.     if (err != noErr)
  1060.         goto cleanup;
  1061.  
  1062.     // { if the selection range contains exactly one embedded object, copy that }
  1063.     if (pWE->selEnd - pWE->selStart == 1 && _WEIsEmbeddedObject(pWE->selStart, &hObjectDesc, hWE))
  1064.     {
  1065.         d[0] = **(AEDesc **)(hObjectDesc);
  1066.         numTypes = 1;
  1067.     }
  1068.     else
  1069.     {
  1070.         // { allocate three zero-length handles to hold the text, the styles and the "soup" }
  1071.         for (i=0; i<3; i++)
  1072.         {
  1073.             err = _WEAllocate(0, kAllocTemp, &d[i].dataHandle);
  1074.             if (err != noErr)
  1075.                 goto cleanup;
  1076.         }
  1077.  
  1078.         // { make a copy of the selection text and styles and create an object "soup" }
  1079.         err = WECopyRange(pWE->selStart, pWE->selEnd, d[0].dataHandle,
  1080.                 d[1].dataHandle, d[2].dataHandle, hWE);
  1081.         if (err != noErr)
  1082.             goto cleanup;
  1083.         
  1084.         // { tag the data }
  1085.         d[0].descriptorType = kTypeText;
  1086.         d[1].descriptorType = kTypeStyles;
  1087.         d[2].descriptorType = kTypeSoup;
  1088.         numTypes = 3;
  1089.         disposeData = true;
  1090.     }
  1091.     
  1092.     // { copy the items to the desk scrap }
  1093.     for (i= 0; i<numTypes; i++)
  1094.     {
  1095.         hItem = d[i].dataHandle;
  1096.         itemSize = GetHandleSize(hItem);
  1097.         if (itemSize > 0)
  1098.         {
  1099.             saveDataLock = _WESetHandleLock(hItem, true);
  1100.             err = PutScrap(itemSize, d[i].descriptorType, *hItem);
  1101.             _WESetHandleLock(hItem, saveDataLock);
  1102.             if (err != noErr)
  1103.                 goto cleanup;
  1104.         }
  1105.     }
  1106.  
  1107. cleanup2:
  1108.     // { clear result code }
  1109.     err = noErr;
  1110.  
  1111. cleanup:
  1112.     // { clean up }
  1113.     if (disposeData)
  1114.     {
  1115.         _WEForgetHandle(&d[0].dataHandle);
  1116.         _WEForgetHandle(&d[1].dataHandle);
  1117.         _WEForgetHandle(&d[2].dataHandle);
  1118.     }
  1119.     _WESetHandleLock((Handle)hWE, saveWELock);
  1120.     return err;
  1121. }
  1122.  
  1123. pascal short WEFeatureFlag(short feature, short action, WEHandle hWE)
  1124. {
  1125.     WEPtr pWE;
  1126.     short retval;
  1127.     
  1128.     pWE = *hWE;
  1129.  
  1130.     // { return current status }
  1131.     retval = BTST(pWE->flags, feature) ? weBitSet : weBitClear;
  1132.  
  1133.     // { set new status if necessary }
  1134.     if (action == weBitClear)
  1135.     { 
  1136.         BCLR(pWE->flags, feature);
  1137.     }
  1138.     else if (action == weBitSet)
  1139.     { 
  1140.         BSET(pWE->flags, feature);
  1141.     }
  1142.     
  1143.     return retval;
  1144. }
  1145.